gusucode.com > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序 > VC++ EMF图片浏览器(可读emf、wmf、emz、wmz、png……等)-源码程序/code/Src/Client/scemflib/SCEMFgdiParser.cpp

    //Download by http://www.NewXing.com
/*
*	This file is part of the EMFexplorer projet.
*	Copyright (C) 2004 Smith Charles.
*
*	This library is free software; you can redistribute it and/or
*	modify it under the terms of the GNU Lesser General Public
*	License as published by the Free Software Foundation; either
*	version 2.1 of the License, or (at your option) any later version.
*
*   This library is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*   Lesser General Public License for more details.
*
*   You should have received a copy of the GNU Lesser General Public
*   License along with this library; if not, write to the Free Software
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
*
*	Extension: for commercial use, apply the Equity Public License, which
*	adds to the normal terms of the GLPL a condition of donation to the author.
*   If you are interested in support for this source code,
*   contact Smith Charles <smith.charles@free.fr> for more information.
*/


#include "stdafx.h"
#include "SCEMFgdiParser.h"
#include "SCEMFRasterizerDefs.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "SCEMFDefs.h"
#include "SCGenInclude.h"
#include SC_INC_WINLIB(SCDCCloner.h)

// to allow token pasting at the beginig of line
#define SC_TKNPASTE(fake)

#define SC_RECORD_DECL0(recType) \
	SC_TKNPASTE(nothing)##recType *pRec = (##recType*)m_pRecord; \
	if (pRec->emr.nSize < sizeof(##recType)) \
		return SC_BRK_NOERROR

#define SC_RECORD_DECL_PLAY(recType) \
	SC_TKNPASTE(nothing)##recType *pRec = (##recType*)m_pRecord; \
	if (pRec->emr.nSize < sizeof(##recType)) \
	{ \
		SCPlayRecord(); \
		return SC_BRK_NOERROR; \
	}

// Reminder
#ifndef SC_RENDERER_CAN_DOROP
// Warning: when m_hPlayDC is not a clone, calling functions like SetMapmode on it may
// perturbate GDI+. This was observed for printer DC.
#pragma message(__FILE__ "(47): SC_RENDERER_CAN_DOROP not defined. Potential printing problems.")
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CSCEMFgdiParser::CSCEMFgdiParser():
	SCBrkEMF(),
	m_pRenderer(NULL),
	m_hDestDC(NULL)
#ifdef SC_USE_DCCLONER
	,m_pCloneDC(NULL)
#endif
{
	memset(&m_xformOrig, 0, sizeof(m_xformOrig));
	m_xformOrig.eM11 = m_xformOrig.eM22 = 1;
}

CSCEMFgdiParser::CSCEMFgdiParser(I_EMFRenderer* pRenderer):
	SCBrkEMF(),
	m_pRenderer(pRenderer),
	m_hDestDC(NULL)
#ifdef SC_USE_DCCLONER
	,m_pCloneDC(NULL)
#endif
{
	memset(&m_xformOrig, 0, sizeof(m_xformOrig));
	m_xformOrig.eM11 = m_xformOrig.eM22 = 1;
}


CSCEMFgdiParser::~CSCEMFgdiParser()
{
#ifdef SC_USE_DCCLONER
	// delete temporary DC
	if (m_pCloneDC)
		delete m_pCloneDC;
#endif
}

void CSCEMFgdiParser::SCResetCracker()
{
}

//////////////////////////////////////////////////////////////////////
// Events methods
//////////////////////////////////////////////////////////////////////
SC_BRKRESULT CSCEMFgdiParser::OnEmfHEADER()
{
	// TRACE0("**EMR_HEADER\n");
	ASSERT(m_pRenderer);

	//ENHMETAHEADER *pRec = (ENHMETAHEADER*)m_pRecord;

	SCPlayRecord();
	m_pRenderer->SCBeginRendering(m_hDestDC, m_hPlayDC);

	return SCBrkEMF::OnEmfHEADER();
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIER()
{
	// TRACE0("**EMR_POLYBEZIER\n");
	ASSERT(m_pRenderer);

	EMRPOLYBEZIER *pRec = (EMRPOLYBEZIER*)m_pRecord;
	m_pRenderer->SCDrawBezier((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYGON()
{
	// TRACE0("**EMR_POLYGON\n");
	ASSERT(m_pRenderer);

	EMRPOLYGON *pRec = (EMRPOLYGON*)m_pRecord;
	m_pRenderer->SCDrawPolygon((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINE()
{
	// TRACE0("**EMR_POLYLINE\n");
	ASSERT(m_pRenderer);

	EMRPOLYLINE *pRec = (EMRPOLYLINE*)m_pRecord;
	m_pRenderer->SCDrawLines((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIERTO()
{
	// TRACE0("**EMR_POLYBEZIERTO\n");
	ASSERT(m_pRenderer);

	EMRPOLYBEZIERTO *pRec = (EMRPOLYBEZIERTO*)m_pRecord;
	m_pRenderer->SCDrawBezierTo((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINETO()
{
	// TRACE0("**EMR_POLYLINETO\n");
	ASSERT(m_pRenderer);

	EMRPOLYLINETO *pRec = (EMRPOLYLINETO*)m_pRecord;
	m_pRenderer->SCDrawLinesTo((LPPOINT)pRec->aptl, pRec->cptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYLINE()
{
	// TRACE0("**EMR_POLYPOLYLINE\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYLINE *pRec = (EMRPOLYPOLYLINE*)m_pRecord;
	LPPOINT pPts = (LPPOINT)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolyline(pPts, pRec->cptl, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYGON()
{
	// TRACE0("**EMR_POLYPOLYGON\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYGON *pRec = (EMRPOLYPOLYGON*)m_pRecord;
	LPPOINT pPts = (LPPOINT)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolygon(pPts, pRec->cptl, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWINDOWEXTEX()
{
	// TRACE0("**EMR_SETWINDOWEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWindowExtent(((EMRSETWINDOWEXTEX*)m_pRecord)->szlExtent.cx,
									((EMRSETWINDOWEXTEX*)m_pRecord)->szlExtent.cy);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWINDOWORGEX()
{
	// TRACE0("**EMR_SETWINDOWORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWindowOrg(((EMRSETWINDOWORGEX*)m_pRecord)->ptlOrigin.x,
								((EMRSETWINDOWORGEX*)m_pRecord)->ptlOrigin.y);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETVIEWPORTEXTEX()
{
	// TRACE0("**EMR_SETVIEWPORTEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetViewportExtent(((EMRSETVIEWPORTEXTEX*)m_pRecord)->szlExtent.cx,
									 ((EMRSETVIEWPORTEXTEX*)m_pRecord)->szlExtent.cy);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETVIEWPORTORGEX()
{
	// TRACE0("**EMR_SETVIEWPORTORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetViewportOrg(((EMRSETVIEWPORTORGEX*)m_pRecord)->ptlOrigin.x,
								  ((EMRSETVIEWPORTORGEX*)m_pRecord)->ptlOrigin.y);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBRUSHORGEX()
{
	// TRACE0("**EMR_SETBRUSHORGEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management (via stretchbltmode and halftone)
#endif

	m_pRenderer->SCSetBrushOrg(((EMRSETBRUSHORGEX*)m_pRecord)->ptlOrigin);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEOF()
{
	// TRACE0("**EMR_EOF\n");
	ASSERT(m_pRenderer);

	// (EMREOF*)m_pRecord;
	SCPlayRecord();
	m_pRenderer->SCEndRendering();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPIXELV()
{
	// TRACE0("**EMR_SETPIXELV\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPixel(((EMRSETPIXELV*)m_pRecord)->ptlPixel,
							 ((EMRSETPIXELV*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMAPPERFLAGS()
{
	// TRACE0("**EMR_SETMAPPERFLAGS\n");
	ASSERT(m_pRenderer);

	// (EMRSETMAPPERFLAGS*)m_pRecord;

	// No aspect ratio management, as we only use TT fonts.
	// Anyway let Windows do it.
	SCPlayRecord();		
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMAPMODE()
{
	// TRACE0("**EMR_SETMAPMODE\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management (via viewport/window settings)
#endif

	m_pRenderer->SCSetMapMode(((EMRSETMAPMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBKMODE()
{
	// TRACE0("**EMR_SETBKMODE\n");
	ASSERT(m_pRenderer);
	//SCPlayRecord(); // wait and see

	m_pRenderer->SCSetBkMode(((EMRSETBKMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPOLYFILLMODE()
{
	// TRACE0("**EMR_SETPOLYFILLMODE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetPolyFillMode(((EMRSETPOLYFILLMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETROP2()
{
	// TRACE0("**EMR_SETROP2\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetROP2(((EMRSETROP2*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETSTRETCHBLTMODE()
{
	// TRACE0("**EMR_SETSTRETCHBLTMODE\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetStretchBltMode(((EMRSETSTRETCHBLTMODE*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTALIGN()
{
	// TRACE0("**EMR_SETTEXTALIGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetTextAlign(((EMRSETTEXTALIGN*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETCOLORADJUSTMENT()
{
	// TRACE0("**EMR_SETCOLORADJUSTMENT\n");
	ASSERT(m_pRenderer);


#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // for ROP (just in case)
#endif
	// "The color adjustment values are used to adjust the input color of the source bitmap
	// for calls to the StretchBlt and StretchDIBits functions when HALFTONE mode is set."
	// But it's NT specific.

	m_pRenderer->SCSetColorAsjustment(&((EMRSETCOLORADJUSTMENT*)m_pRecord)->ColorAdjustment);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTCOLOR()
{
	// TRACE0("**EMR_SETTEXTCOLOR\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetTextColor(((EMRSETTEXTCOLOR*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETBKCOLOR()
{
	// TRACE0("**EMR_SETBKCOLOR\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetBkColor(((EMRSETBKCOLOR*)m_pRecord)->crColor);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfOFFSETCLIPRGN()
{
	// TRACE0("**EMR_OFFSETCLIPRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCOffsetClipRect(((EMROFFSETCLIPRGN*)m_pRecord)->ptlOffset);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMOVETOEX()
{
	// TRACE0("**EMR_MOVETOEX\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCMoveToEx(((EMRMOVETOEX*)m_pRecord)->ptl);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMETARGN()
{
	// TRACE0("**EMR_SETMETARGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCOnSetMetaRgn();
	
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXCLUDECLIPRECT()
{
	// TRACE0("**EMR_EXCLUDECLIPRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCExcludeClipRect(((EMREXCLUDECLIPRECT*)m_pRecord)->rclClip);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfINTERSECTCLIPRECT()
{
	// TRACE0("**EMR_INTERSECTCLIPRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCIntersectClipRect(((EMRINTERSECTCLIPRECT*)m_pRecord)->rclClip);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSCALEVIEWPORTEXTEX()
{
	// TRACE0("**EMR_SCALEVIEWPORTEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	EMRSCALEVIEWPORTEXTEX *pRec = (EMRSCALEVIEWPORTEXTEX*)m_pRecord;
	m_pRenderer->SCScaleViewport(pRec->xNum, pRec->xDenom, pRec->yNum, pRec->yDenom);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSCALEWINDOWEXTEX()
{
	// TRACE0("**EMR_SCALEWINDOWEXTEX\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	EMRSCALEWINDOWEXTEX *pRec = (EMRSCALEWINDOWEXTEX*)m_pRecord;
	m_pRenderer->SCScaleWindow(pRec->xNum, pRec->xDenom, pRec->yNum, pRec->yDenom);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSAVEDC()
{
	// TRACE0("**EMR_SAVEDC\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	m_pRenderer->SCOnDCSaved();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRESTOREDC()
{
	// TRACE0("**EMR_RESTOREDC\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();
	
	// Be careful : sometimes we get bad values from the record.
	// Some documents contain values like 30,
	// though there is only one DC state on the stack
	m_pRenderer->SCOnDCRestored(((EMRRESTOREDC*)m_pRecord)->iRelative);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETWORLDTRANSFORM()
{
	// TRACE0("**EMR_SETWORLDTRANSFORM\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCSetWorldTransform(((EMRSETWORLDTRANSFORM*)m_pRecord)->xform);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMODIFYWORLDTRANSFORM()
{
	// TRACE0("**EMR_MODIFYWORLDTRANSFORM\n");
	ASSERT(m_pRenderer);

#ifndef SC_RENDERER_CAN_DOROP
	SCPlayRecord(); // required for ROP management
#endif

	m_pRenderer->SCModifyWorldTransform(((EMRMODIFYWORLDTRANSFORM*)m_pRecord)->xform,
										((EMRMODIFYWORLDTRANSFORM*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTOBJECT()
{
	// TRACE0("**EMR_SELECTOBJECT\n");
	ASSERT(m_pRenderer);

	// First, play the record to realize GDI objects
	SCPlayRecord();

	// Then extract objects characteristics
	EMRSELECTOBJECT *pRec = (EMRSELECTOBJECT*)m_pRecord;
	HGDIOBJ hGdiObj;
	// Is the high order bit set?
	if (pRec->ihObject & 0x80000000)
	{
		// High order bit is set - its a stock object
		// Strip the high bit to get the index
		DWORD dwIndex = pRec->ihObject & 0x7fffffff;
		// Pass the index to GetStockObject()
		hGdiObj = GetStockObject( dwIndex );
	} else
		// High order bit isn't set - not a stock object
		hGdiObj = (HGDIOBJ)m_lpEnumHandleTable->objectHandle[pRec->ihObject];

	long lType = GetObjectType(hGdiObj);
	switch(lType)
	{
		case OBJ_EXTPEN:
			m_pRenderer->SCOnChangeExtPen((HPEN)hGdiObj);
			break;

		case OBJ_PEN:
			m_pRenderer->SCOnChangePen((HPEN)hGdiObj);
			break;

		case OBJ_BRUSH:
			m_pRenderer->SCOnChangeBrush((HBRUSH)hGdiObj);
			break;
			
		case OBJ_FONT:
			m_pRenderer->SCOnChangeFont((HFONT)hGdiObj);
			break;
			
		//case OBJ_PAL:
		// unlikely to occur (The SelectObject function does not work with palettes)
		//m_pRenderer->SCOnChangePalette((HPALETTE)hGdiObj);
		//break;								
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEPEN()
{
	// TRACE0("**EMR_CREATEPEN\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEPEN *pRec = (EMRCREATEPEN*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEBRUSHINDIRECT()
{
	// TRACE0("**EMR_CREATEBRUSHINDIRECT\n");
	ASSERT(m_pRenderer);

	// Note: due to dithering, Windows may change BS_SOLID in BS_PATTERN
	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEBRUSHINDIRECT *pRec = (EMRCREATEBRUSHINDIRECT*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDELETEOBJECT()
{
	// TRACE0("**EMR_DELETEOBJECT\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object deletion: do nothing
	// EMRDELETEOBJECT *pRec = (EMRDELETEOBJECT*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfANGLEARC()
{
	// TRACE0("**EMR_ANGLEARC\n");
	ASSERT(m_pRenderer);

	EMRANGLEARC *pRec = (EMRANGLEARC*)m_pRecord;
	m_pRenderer->SCDrawAngleArc((LPPOINT)&pRec->ptlCenter, pRec->nRadius, pRec->eStartAngle, pRec->eSweepAngle);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfELLIPSE()
{
	// TRACE0("**EMR_ELLIPSE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawEllipse((LPCRECT)&((EMRELLIPSE*)m_pRecord)->rclBox);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRECTANGLE()
{
	// TRACE0("**EMR_RECTANGLE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawRectangle((LPCRECT)&((EMRRECTANGLE*)m_pRecord)->rclBox);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfROUNDRECT()
{
	// TRACE0("**EMR_ROUNDRECT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawRoundRectangle((LPCRECT)&((EMRROUNDRECT*)m_pRecord)->rclBox,
												((EMRROUNDRECT*)m_pRecord)->szlCorner);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfARC()
{
	// TRACE0("**EMR_ARC\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawArc(&((EMRARC*)m_pRecord)->rclBox,
						   &((EMRARC*)m_pRecord)->ptlStart,
						   &((EMRARC*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCHORD()
{
	// TRACE0("**EMR_CHORD\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawChord(&((EMRCHORD*)m_pRecord)->rclBox,
							 &((EMRCHORD*)m_pRecord)->ptlStart,
							 &((EMRCHORD*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPIE()
{
	// TRACE0("**EMR_PIE\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPie(&((EMRPIE*)m_pRecord)->rclBox,
						   &((EMRPIE*)m_pRecord)->ptlStart,
						   &((EMRPIE*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;

}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTPALETTE()
{
	// TRACE0("**EMR_SELECTPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	m_pRenderer->SCOnChangePalette((HPALETTE)((EMRSELECTPALETTE*)m_pRecord)->ihPal);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEPALETTE()
{
	// TRACE0("**EMR_CREATEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRCREATEPALETTE *pRec = (EMRCREATEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETPALETTEENTRIES()
{
	// TRACE0("**EMR_SETPALETTEENTRIES\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRSETPALETTEENTRIES *pRec = (EMRSETPALETTEENTRIES*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfRESIZEPALETTE()
{
	// TRACE0("**EMR_RESIZEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRRESIZEPALETTE *pRec = (EMRRESIZEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfREALIZEPALETTE()
{
	// TRACE0("**EMR_REALIZEPALETTE\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// Object creation: do nothing
	// EMRREALIZEPALETTE *pRec = (EMRREALIZEPALETTE*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTFLOODFILL()
{
	// TRACE0("**EMR_EXTFLOODFILL\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFloodFill(((EMREXTFLOODFILL*)m_pRecord)->ptlStart,
							 ((EMREXTFLOODFILL*)m_pRecord)->crColor,
							 ((EMREXTFLOODFILL*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfLINETO()
{
	// TRACE0("**EMR_LINETO\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesTo((LPPOINT)&((EMRLINETO*)m_pRecord)->ptl, 1);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfARCTO()
{
	// TRACE0("**EMR_ARCTO\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawArcTo(&((EMRARCTO*)m_pRecord)->rclBox,
							 &((EMRARCTO*)m_pRecord)->ptlStart,
							 &((EMRARCTO*)m_pRecord)->ptlEnd);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYDRAW()
{
	// TRACE0("**EMR_POLYDRAW\n");
	ASSERT(m_pRenderer);

	EMRPOLYDRAW *pRec = (EMRPOLYDRAW*)m_pRecord;

	BYTE* pTypes = (BYTE*)((POINTL*)pRec->aptl + pRec->cptl);
	m_pRenderer->SCDrawPolyDraw((POINT*)pRec->aptl,
								pRec->cptl,
								pTypes);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETARCDIRECTION()
{
	// TRACE0("**EMR_SETARCDIRECTION\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetArcDirection(((EMRSETARCDIRECTION*)m_pRecord)->iArcDirection);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETMITERLIMIT()
{
	// TRACE0("**EMR_SETMITERLIMIT\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSetMiterLimit(((EMRSETMITERLIMIT*)m_pRecord)->eMiterLimit);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfBEGINPATH()
{
	// TRACE0("**EMR_BEGINPATH\n");
	ASSERT(m_pRenderer);

	//EMRBEGINPATH *pRec = (EMRBEGINPATH*)m_pRecord;

	m_pRenderer->SCBeginPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfENDPATH()
{
	// TRACE0("**EMR_ENDPATH\n");
	ASSERT(m_pRenderer);

	//EMRENDPATH *pRec = (EMRENDPATH*)m_pRecord;

	m_pRenderer->SCEndPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCLOSEFIGURE()
{
	// TRACE0("**EMR_CLOSEFIGURE\n");
	ASSERT(m_pRenderer);

	//EMRCLOSEFIGURE *pRec = (EMRCLOSEFIGURE*)m_pRecord;

	m_pRenderer->SCCloseFigure();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFILLPATH()
{
	// TRACE0("**EMR_FILLPATH\n");
	ASSERT(m_pRenderer);

	//EMRFILLPATH *pRec = (EMRFILLPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_FILL);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTROKEANDFILLPATH()
{
	// TRACE0("**EMR_STROKEANDFILLPATH\n");
	ASSERT(m_pRenderer);

	//EMRSTROKEANDFILLPATH *pRec = (EMRSTROKEANDFILLPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_STROKEANDFILL);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTROKEPATH()
{
	// TRACE0("**EMR_STROKEPATH\n");
	ASSERT(m_pRenderer);

	//EMRSTROKEPATH *pRec = (EMRSTROKEPATH*)m_pRecord;

	// Note: Don't know what the rclBounds is doing in the record
	m_pRenderer->SCApplyPath(SC_PATH_STROKE);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFLATTENPATH()
{
	// TRACE0("**EMR_FLATTENPATH\n");
	ASSERT(m_pRenderer);

	//EMRFLATTENPATH *pRec = (EMRFLATTENPATH*)m_pRecord;

	m_pRenderer->SCFlattenPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfWIDENPATH()
{
	// TRACE0("**EMR_WIDENPATH\n");
	ASSERT(m_pRenderer);

	//EMRWIDENPATH *pRec = (EMRWIDENPATH*)m_pRecord;

	m_pRenderer->SCWidenPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSELECTCLIPPATH()
{
	// TRACE0("**EMR_SELECTCLIPPATH\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCApplyPath(SC_PATH_CLIP, ((EMRSELECTCLIPPATH*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfABORTPATH()
{
	// TRACE0("**EMR_ABORTPATH\n");
	ASSERT(m_pRenderer);

	//EMRABORTPATH *pRec = (EMRABORTPATH*)m_pRecord;

	m_pRenderer->SCAbortPath();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGDICOMMENT()
{
	// TRACE0("**EMR_GDICOMMENT\n");
	ASSERT(m_pRenderer);

#pragma message( __FILE__  "(894): TODO: Recurse parser on embedded WMF? ")
#if 0
	EMRGDICOMMENT *pRec = (EMRGDICOMMENT*)m_pRecord;

	DWORD* pDW = (DWORD*)pRec->Data;
	if (GDICOMMENT_IDENTIFIER==*pDW)
	{
		pDW++;
		switch(*pDW)
		{
		case GDICOMMENT_WINDOWS_METAFILE:
			// embedded WMF
				// TODO:
			break;
			
		case GDICOMMENT_MULTIFORMATS:
			// embedded formats
				// TODO:
			break;
		}
	}
#endif

	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFILLRGN()
{
	// TRACE0("**EMR_FILLRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFillRgn((HBRUSH)m_lpEnumHandleTable->objectHandle[((EMRFILLRGN*)m_pRecord)->ihBrush],
						   ((EMRFILLRGN*)m_pRecord)->RgnData,
						   ((EMRFILLRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFRAMERGN()
{
	// TRACE0("**EMR_FRAMERGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCFrameRgn((HBRUSH)m_lpEnumHandleTable->objectHandle[((EMRFRAMERGN*)m_pRecord)->ihBrush],
							((EMRFRAMERGN*)m_pRecord)->szlStroke,
							((EMRFRAMERGN*)m_pRecord)->RgnData,
							((EMRFRAMERGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfINVERTRGN()
{
	// TRACE0("**EMR_INVERTRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCInvertRgn(((EMRINVERTRGN*)m_pRecord)->RgnData,
							 ((EMRINVERTRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPAINTRGN()
{
	// TRACE0("**EMR_PAINTRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCPaintRgn(((EMRPAINTRGN*)m_pRecord)->RgnData,
							((EMRPAINTRGN*)m_pRecord)->cbRgnData);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTSELECTCLIPRGN()
{
	// TRACE0("**EMR_EXTSELECTCLIPRGN\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCSelectClipRgn(((EMREXTSELECTCLIPRGN*)m_pRecord)->RgnData,
								 ((EMREXTSELECTCLIPRGN*)m_pRecord)->cbRgnData,
								 ((EMREXTSELECTCLIPRGN*)m_pRecord)->iMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfBITBLT()
{
	// TRACE0("**EMR_BITBLT\n");
	ASSERT(m_pRenderer);

	EMRBITBLT *pRec = (EMRBITBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source and Destination have same size for BitBlt function
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxDest, pRec->ySrc + pRec->cyDest);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTRETCHBLT()
{
	// TRACE0("**EMR_STRETCHBLT\n");
	ASSERT(m_pRenderer);

	EMRSTRETCHBLT *pRec = (EMRSTRETCHBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfMASKBLT()
{
	// TRACE0("**EMR_MASKBLT\n");
	ASSERT(m_pRenderer);

	EMRMASKBLT *pRec = (EMRMASKBLT*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// the source bitmap
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// the mask bitmap is optional
		BITMAPINFO *pBmiMask = NULL;
		BYTE *pBitsMask = NULL;
		if (pRec->offBmiMask && pRec->offBitsMask)
		{
			pBmiMask = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiMask); 
			pBitsMask = (BYTE *)((BYTE *)pRec + pRec->offBitsMask);
		}

		// Source equals dest in dimensions
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxDest, pRec->ySrc + pRec->cyDest);

		m_pRenderer->SCDrawImageMsk(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop, &pRec->xformSrc,
		pRec->crBkColorSrc, pRec->xMask, pRec->yMask, pBitsMask, pBmiMask, pRec->iUsageMask);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop, &pRec->xformSrc);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPLGBLT()
{
	// TRACE0("**EMR_PLGBLT\n");
	ASSERT(m_pRenderer);

	EMRPLGBLT *pRec = (EMRPLGBLT*)m_pRecord;

	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// the source bitmap
		BITMAPINFO *pBmi = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits = (BYTE *)((BYTE *)pRec + pRec->offBitsSrc);

		// the mask bitmap is optional
		BITMAPINFO *pBmiMask = NULL;
		BYTE *pBitsMask = NULL;
		if (pRec->offBmiMask && pRec->offBitsMask)
		{
			pBmiMask = (BITMAPINFO *)((BYTE *)pRec + pRec->offBmiMask); 
			pBitsMask = (BYTE *)((BYTE *)pRec + pRec->offBitsMask);
		}

		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);

		m_pRenderer->SCDrawImagePlg(pRec->aptlDest,
		&RcSrc, pBits, pBmi, pRec->iUsageSrc, &pRec->xformSrc, pRec->crBkColorSrc,
		pRec->xMask, pRec->yMask, pBitsMask, pBmiMask, pRec->iUsageMask);
		
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETDIBITSTODEVICE()
{
	// TRACE0("**EMR_SETDIBITSTODEVICE\n");
	ASSERT(m_pRenderer);

	EMRSETDIBITSTODEVICE *pRec = (EMRSETDIBITSTODEVICE*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxSrc, pRec->yDest + pRec->cySrc);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, SRCCOPY);
	} else
		m_pRenderer->SCPatBlt(&RcDest, SRCCOPY);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTRETCHDIBITS()
{
	ASSERT(m_pRenderer);

	// TRACE0("**EMR_STRETCHDIBITS\n");
	EMRSTRETCHDIBITS *pRec = (EMRSTRETCHDIBITS*)m_pRecord;

	// Destination
	CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);
	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		m_pRenderer->SCDrawImage(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc, pRec->dwRop);
	} else
		m_pRenderer->SCPatBlt(&RcDest, pRec->dwRop);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTCREATEFONTINDIRECTW()
{
	// TRACE0("**EMR_EXTCREATEFONTINDIRECTW\n");
	ASSERT(m_pRenderer);

	// Play the record on the DC, in order to get information about the physical font,
	// which is the font really used by the DC.
#if 0
	// Raster and vector fonts should be replaced by substitutes.

	// Example of early font substitution.
	// This is cleaner than late substitution (SCTTFontFromLOGFONT in SCGdiplusUtils.cpp);
	// but it penalizes good TT fonts
	// (memory allocations and string comparisons)
	EMREXTCREATEFONTINDIRECTW *pSubs = (EMREXTCREATEFONTINDIRECTW *)SCCloneRecord();
	if (pSubs)
	{
		LOGFONTW& rLogFont = pSubs->elfw.elfLogFont;
		DWORD dwFamily = SCFontFamilyApproximantW((WCHAR*)rLogFont.lfFaceName);
		if (FF_DONTCARE!=dwFamily)
		{
			rLogFont.lfPitchAndFamily |= dwFamily;
			rLogFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
		}
		
		SCPlayRecordAndFree(pSubs);
	} else
		SCPlayRecord();
#else
	SCPlayRecord();
#endif
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTTEXTOUTA()
{
	// TRACE0("**EMR_EXTTEXTOUTA or **EMR_EXTTEXTOUTW\n");
	ASSERT(m_pRenderer);

	SC_BRKRESULT error(SC_BRK_NOERROR);

	// the structure EMREXTTEXTOUTA is same as EMREXTTEXTOUTW
	EMREXTTEXTOUTA *pRec = (EMREXTTEXTOUTA*)m_pRecord;

	// get the EMRTEXT
	EMRTEXT *pTextRec = &(pRec->emrtext);

	// number of characters	
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
	{
		// We don't know the behavior of ExtTextOut:
		// - if number of characters is 0
		// - if, moreover, TA_UPDATECP is set in the DC
#if 0
		// if required, play the record
		SCPlayRecord();
#endif
		return 0;
	}

	UINT uiOptions = pTextRec->fOptions; // we might have to alter it

	// Build the string
	LPWSTR pwStr = NULL;
	if (pRec->emr.iType == EMR_EXTTEXTOUTW) 
	{
		// The string is in unicode -> we let it as is.
		// Offset is from the beginning of the struct and is in bytes.
		// 	
		// Get the unicode string
		pwStr = (LPWSTR) ((BYTE *)pRec + pTextRec->offString);

#if 0
		// TODO:
		// Translate glyph indices to code points when the rasterizer does not
		// support string measurement for glyph indices.
		if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
		{
			int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pString, iNbChars);
			if (iRes)
				uiOptions &= ~ETO_GLYPH_INDEX;
		}
#endif
	} else
	{
		// The string is single-byte encoded.
		// Get adress of the string	then construct the string.			
		LPSTR pString = (char *)pRec + pTextRec->offString;
		pwStr = new wchar_t[iNbChars+1];
		if (pwStr)
		{// We will use UNICODE strings exclusively
			pwStr[iNbChars] = 0;
			int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pString, iNbChars, pwStr, iNbChars+1);
			ASSERT(iNum==iNbChars);
		}
	}
	ASSERT(pwStr);

	// Get intercharacter spacing array.
	// Be careful ! there are cases where OffDx==0.
	DWORD OffDx = pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	if (0==OffDx)
	{
		// If the lpDx parameter is NULL, the ExtTextOut function uses the default
		// spacing between characters. So we do.
		//
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	}
	else
	{ // else use distances between origins of adjacent character cells as passed to ExtTextOut.
		// except that, sometimes, the last character is given a spacing of 0, which is
		// not good for us if this character has a non-null width.
		if (0==lpDx[iNbChars-1])
		{
			INT* pInts = new INT[iNbChars];
			memmove(pInts, lpDx, iNbChars*sizeof(INT));

			// compute only the last character's width
			BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr+iNbChars-1, 1, pInts+iNbChars-1);
			ASSERT(bOk);
			lpDx = pInts;
			OffDx = 0; // for cleanup
			//at this point, 0 is accepted
			//ASSERT(lpDx[iNbChars-1]);
		}
	}

#if 0
	// This thing is not accurate. We prefer to let the rasterizer use its computed rectangle
	// when no opaquing rectangle is supplied
	CRect rcText = (LPCRECT)&pTextRec->rcl;
	if (rcText.IsRectEmpty())
	{
		rcText.CopyRect((LPCRECT)&pRec->rclBounds);
		DPtoLP(m_hPlayDC, (LPPOINT)&rcText, 2);
	}
	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&rcText, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&rcText, pwStr, iNbChars, lpDx, 1, 1);
#else
	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, 1, 1);
#endif

	// Clean up
	if (pRec->emr.iType == EMR_EXTTEXTOUTA) 
		delete [] pwStr;
	if (0==OffDx)
		delete [] lpDx;

	return error;
}

//////////////////////////////////////////////////////////////////////////////////////
// It is unlikely that SCEmfTextoutA/SCEmfTextoutW are called. So I prefer to let
// them separated (with duplicated code). See OnEmfTextoutA for documentation
SC_BRKRESULT CSCEMFgdiParser::SCEmfTextoutA(EMREXTTEXTOUTA* pRec, EMRTEXT* pTextRec)
{
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
		return SC_BRK_NOERROR;

	// Get adress of the string	then construct the widestring.			
	LPWSTR pwStr = new wchar_t[iNbChars + 1];
	if (pwStr)
	{// We will use UNICODE strings exclusively
		pwStr[iNbChars] = 0;
		int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)((char *)pRec + pTextRec->offString),
			iNbChars, pwStr, iNbChars + 1);
		ASSERT(iNum==iNbChars);
		
		SCFinishTextout(pRec, pTextRec, pwStr, pTextRec->fOptions);
		
		// Clean up
		delete [] pwStr;
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::SCEmfTextoutW(EMREXTTEXTOUTW* pRec, EMRTEXT* pTextRec)
{
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
		return SC_BRK_NOERROR;

	UINT uiOptions = pTextRec->fOptions; // we might have to alter it

	// Get the unicode string
	LPWSTR pwStr = (LPWSTR) ((BYTE *)pRec + pTextRec->offString);

#if 0
	ASSERT(m_pRenderer);

	// TODO:
	// Translate glyph indices to code points when the rasterizer does not
	// support string measurement for glyph indices.
	if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
	{
		int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pwStr, iNbChars);
		if (iRes)
			uiOptions &= ~ETO_GLYPH_INDEX;
	}
#endif

	SCFinishTextout(pRec, pTextRec, pwStr, uiOptions);
	return SC_BRK_NOERROR;
}

void CSCEMFgdiParser::SCFinishTextout(EMREXTTEXTOUTA* pRec, EMRTEXT* pTextRec, LPWSTR pwStr, UINT uiOptions)
{
	ASSERT(m_pRenderer);

	INT iNbChars = pTextRec->nChars;
	// Get intercharacter spacing array.
	DWORD OffDx = pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	if (0==OffDx)
	{
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	}
	else
	{ // else use distances between origins of adjacent character cells as passed to ExtTextOut.
		// except that, sometimes, the last character is given a spacing of 0, which is not good for us.
		if (0==lpDx[iNbChars-1])
		{
			INT* pInts = new INT[iNbChars];
			memmove(pInts, lpDx, iNbChars*sizeof(INT));

			// compute only the last character's width
			BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr+iNbChars-1, 1, pInts+iNbChars-1);
			ASSERT(bOk);
			lpDx = pInts;
			OffDx = 0; // for cleanup
			ASSERT(lpDx[iNbChars-1]);
		}
	}

	if (GM_COMPATIBLE==pRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPCRECT)&pTextRec->rcl, pwStr, iNbChars, lpDx, 1, 1);

	// Clean up
	if (0==OffDx)
		delete [] lpDx;
}
///////////////////////////////////////////////////////////////////////////////////////////

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTTEXTOUTW()
{
	// TRACE0("**EMR_EXTTEXTOUTW\n");
	//EMREXTTEXTOUTW *pRec = (EMREXTTEXTOUTW*)m_pRecord;

	// pass the record to 
	return OnEmfEXTTEXTOUTA();
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIER16()
{
	// TRACE0("**EMR_POLYBEZIER16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawBezierS(((EMRPOLYBEZIER16*)m_pRecord)->apts,
							  ((EMRPOLYBEZIER16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYGON16()
{
	// TRACE0("**EMR_POLYGON16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawPolygonS(((EMRPOLYGON16*)m_pRecord)->apts,
								((EMRPOLYGON16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINE16()
{
	// TRACE0("**EMR_POLYLINE16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesS(((EMRPOLYLINE16*)m_pRecord)->apts,
							 ((EMRPOLYLINE16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYBEZIERTO16()
{
	// TRACE0("**EMR_POLYBEZIERTO16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawBezierToS(((EMRPOLYBEZIERTO16*)m_pRecord)->apts,
								((EMRPOLYBEZIERTO16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYLINETO16()
{
	// TRACE0("**EMR_POLYLINETO16\n");
	ASSERT(m_pRenderer);

	m_pRenderer->SCDrawLinesToS(((EMRPOLYLINETO16*)m_pRecord)->apts,
								((EMRPOLYLINETO16*)m_pRecord)->cpts);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYLINE16()
{
	// TRACE0("**EMR_POLYPOLYLINE16\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYLINE16 *pRec = (EMRPOLYPOLYLINE16*)m_pRecord;
	LPPOINTS pPts = (LPPOINTS)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolylineS(pPts, pRec->cpts, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYPOLYGON16()
{
	// TRACE0("**EMR_POLYPOLYGON16\n");
	ASSERT(m_pRenderer);

	EMRPOLYPOLYGON16 *pRec = (EMRPOLYPOLYGON16*)m_pRecord;
	LPPOINTS pPts = (LPPOINTS)((DWORD*)pRec->aPolyCounts + pRec->nPolys);
	m_pRenderer->SCDrawPolyPolygonS(pPts, pRec->cpts, (DWORD*)pRec->aPolyCounts, pRec->nPolys);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYDRAW16()
{
	// TRACE0("**EMR_POLYDRAW16\n");
	ASSERT(m_pRenderer);

	EMRPOLYDRAW16 *pRec = (EMRPOLYDRAW16*)m_pRecord;

	BYTE* pTypes = (BYTE*)((POINTS*)pRec->apts + pRec->cpts);
	m_pRenderer->SCDrawPolyDrawS(pRec->apts,
								pRec->cpts,
								pTypes);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEMONOBRUSH()
{
	// TRACE0("**EMR_CREATEMONOBRUSH\n");
	ASSERT(m_pRenderer);

	EMRCREATEMONOBRUSH *pRec = (EMRCREATEMONOBRUSH*)m_pRecord;

	// SCPlayRecord() won't work: the brush will reference a deleted bitmap (XP specific bug?).
	// See comments in the final block.
	SCPlayRecord();

	#ifndef DIB_PAL_INDICES
		#define DIB_PAL_INDICES	2
	#endif
	ASSERT(DIB_PAL_INDICES==pRec->iUsage);

	// DIB_PAL_INDICES management. Special case: the DIB has no color table.
	BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmi); 
	DWORD *pBitsDW     = (DWORD *) ((BYTE *) pRec + pRec->offBits);
	// Attach a monochrome palette to the DIB specification,
	// and create a device-dependent bitmap
	HBITMAP hBm = NULL;
	DWORD dwSize = pBmi->bmiHeader.biSize + 2*sizeof(RGBQUAD);
	BITMAPINFO* pBmi2 = (BITMAPINFO*) new BYTE[dwSize];
	memmove(pBmi2, pBmi, dwSize);
	
	HDC hMonoDC = CreateCompatibleDC(m_hPlayDC); // must be monochrome
	ASSERT(hMonoDC);
	SCFillMonochromePalette(hMonoDC, (PPALETTEENTRY)pBmi2->bmiColors);

	// May create resource leak, as we don't call DeleteObject on hBm (see comments in the final block)
	hBm = CreateDIBitmap(hMonoDC, &pBmi2->bmiHeader, CBM_INIT, pBitsDW, pBmi2, DIB_RGB_COLORS);
	
	DeleteDC(hMonoDC);
	delete [] (BYTE*)pBmi2;
	ASSERT(hBm);

#ifdef _DEBUG
	{// check
		BITMAP bm;
		int iRes = GetObject(hBm, sizeof(BITMAP), &bm);
		ASSERT(iRes);
	}
#endif

	HBRUSH hBrush = CreatePatternBrush(hBm);
	ASSERT(hBrush);
	m_lpEnumHandleTable->objectHandle[pRec->ihBrush] = hBrush;

#if 0
	// Don't do this normal step of the code:
	//		DeleteObject(hBm);
	// It seems that XP's CreatePatternBrush does not make a copy of the given bitmap.
	// To prove it, activate this code, and you will see that GetObjectType
	// will fail on the LogBrushVerif2.lbHatch handle returned by GetObject.
	{
		// Check before delete
		LOGBRUSH LogBrushVerif1;
		ASSERT(::GetObject(hBrush, sizeof(LogBrushVerif1), &LogBrushVerif1));
		ASSERT(LogBrushVerif1.lbStyle==BS_PATTERN);
		int iType1 = GetObjectType((HGDIOBJ)LogBrushVerif1.lbHatch);
		ASSERT(iType1==OBJ_BITMAP);
		
		// Suppose we delete it
		DeleteObject(hBm);
		
		// Check after delete
		LOGBRUSH LogBrushVerif2;
		ASSERT(::GetObject(hBrush, sizeof(LogBrushVerif2), &LogBrushVerif2));
		ASSERT(LogBrushVerif2.lbStyle==BS_PATTERN);
		int iType2 = GetObjectType((HGDIOBJ)LogBrushVerif2.lbHatch);
		ASSERT(iType2==OBJ_BITMAP); // fails
	}
#endif

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATEDIBPATTERNBRUSHPT()
{
	// TRACE0("**EMR_CREATEDIBPATTERNBRUSHPT\n");
	ASSERT(m_pRenderer);

#if 0
	// On NT.SP5, Same problem as monobrush
	SCPlayRecord();
#else
	EMRCREATEDIBPATTERNBRUSHPT *pRec = (EMRCREATEDIBPATTERNBRUSHPT*)m_pRecord;
	ASSERT(DIB_PAL_COLORS==pRec->iUsage || DIB_RGB_COLORS==pRec->iUsage);
	BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmi);

	// Note: On Windows 95 and Windows 98, "creating brushes from bitmaps or DIBs
	//	larger than 8x8 pixels is not supported". So we should restrict the pattern to 8x8.
	// But what if the EMF was created on NT/2K/XP? It seems that GDI doesn't shrink the
	// pattern in order to render the EMF OS-independently.

	DWORD dwSize = pRec->cbBmi + pRec->cbBits;
	// should agree with:
	// BITMAPINFOHEADER* pBmih = &pBmi->bmiHeader;
	// DWORD dwSize = pBmih->biSize +
	//						ColorTableSize(pBmih) +
	//						((pBmih->biSizeImage) ? pBmih->biSizeImage :
	//						abs(pBmih->biHeight)*WIDTHBYTES(pBmih->biWidth*pBmih->biBitCount));

	// May create resource leak, as we don't call GlobalFree on hMem
	// (in fact, this is the sole purpose of this code)
	HGLOBAL hMem = GlobalAlloc(GPTR, dwSize);
	BITMAPINFO* pBmi2 = (BITMAPINFO*)GlobalLock(hMem);
	memmove(pBmi2, pBmi, dwSize);

	HBRUSH hBrush = CreateDIBPatternBrushPt(pBmi2, pRec->iUsage);

	GlobalUnlock(hMem);
	ASSERT(hBrush);
	m_lpEnumHandleTable->objectHandle[pRec->ihBrush] = hBrush;

	#ifdef _DEBUG
	{// Check after play
		LOGBRUSH LogBrushVerif;
		ASSERT(::GetObject((HBRUSH)m_lpEnumHandleTable->objectHandle[pRec->ihBrush], sizeof(LogBrushVerif), &LogBrushVerif));
		ASSERT(LogBrushVerif.lbStyle==BS_DIBPATTERN);
	}
	#endif
#endif
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTCREATEPEN()
{
	// TRACE0("**EMR_EXTCREATEPEN\n");
	ASSERT(m_pRenderer);

	SCPlayRecord();

	// EMREXTCREATEPEN *pRec = (EMREXTCREATEPEN*)m_pRecord;
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYTEXTOUTA()
{
	// TRACE0("**EMR_POLYTEXTOUTA\n");
	ASSERT(m_pRenderer);

	EMRPOLYTEXTOUTA *pRec = (EMRPOLYTEXTOUTA*)m_pRecord;
	for (int i=0; (i<pRec->cStrings); i++)
	{
		SCEmfTextoutA((EMREXTTEXTOUTA*)pRec, &pRec->aemrtext[i]);
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPOLYTEXTOUTW()
{
	// TRACE0("**EMR_POLYTEXTOUTW\n");
	ASSERT(m_pRenderer);

	EMRPOLYTEXTOUTW *pRec = (EMRPOLYTEXTOUTW*)m_pRecord;
	for (int i=0; (i<pRec->cStrings); i++)
	{
		SCEmfTextoutW((EMREXTTEXTOUTW*)pRec, &pRec->aemrtext[i]);
	}

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMMODE()
{
	// TRACE0("**EMR_SETICMMODE\n");
	ASSERT(m_pRenderer);

//		EMRSETICMMODE *pRec = (EMRSETICMMODE*)m_pRecord;

	// MSDN: see "DIBINFO.C"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATECOLORSPACE()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRCREATECOLORSPACE *pRec = (EMRCREATECOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCREATECOLORSPACEW()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

	//EMRCREATECOLORSPACEW *pRec = (EMRCREATECOLORSPACEW*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETCOLORSPACE()
{
	// TRACE0("**EMR_SETCOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRSELECTCOLORSPACE *pRec = (EMRSELECTCOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDELETECOLORSPACE()
{
	// TRACE0("**EMR_DELETECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		EMRDELETECOLORSPACE *pRec = (EMRDELETECOLORSPACE*)m_pRecord;

	// MSDN: see "Basic ICM 2.0 Functions for Use Within a Device Context"
	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGLSRECORD()
{
	// TRACE0("**EMR_GLSRECORD\n");
	ASSERT(m_pRenderer);

	SC_BRKRESULT error(SC_BRK_NOERROR);
	EMRGLSRECORD *pRec = (EMRGLSRECORD*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return error;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGLSBOUNDEDRECORD()
{
	// TRACE0("**EMR_GLSBOUNDEDRECORD\n");
	ASSERT(m_pRenderer);

//		EMRGLSBOUNDEDRECORD *pRec = (EMRGLSBOUNDEDRECORD*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfPIXELFORMAT()
{
	// TRACE0("**EMR_PIXELFORMAT\n");
	ASSERT(m_pRenderer);

//		EMRPIXELFORMAT *pRec = (EMRPIXELFORMAT*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfDRAWESCAPE()
{
	// TRACE0("**EMR_DRAWESCAPE\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_105
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfEXTESCAPE()
{
	// TRACE0("**EMR_EXTESCAPE\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_106
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSTARTDOC()
{
	// TRACE0("**EMR_STARTDOC\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Do not play Escape
	// Anyway, now it's SCEMF_RESERVED_107
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSMALLTEXTOUT()
{
	// TRACE0("**EMR_SMALLTEXTOUT\n");
	ASSERT(m_pRenderer);

	// WARNING: it's SCEMF_RESERVED_108
	SCEMRSMALLTEXTOUTA *pRec = (SCEMRSMALLTEXTOUTA*)m_pRecord;
	// TODO: review this line
	SCEMRSMALLTEXTOUTA2 *pTextRec2 = (SCEMRSMALLTEXTOUTA2*)m_pRecord;

	// get the EMRTEXT
	SCEMRSMALLTEXTOUTA *pTextRec = pRec;

	// number of characters	
	INT iNbChars = pTextRec->nChars;
	if (iNbChars<=0)
	{
		// We don't know the behavior of ExtTextOut:
		// - if number of characters is 0
		// - if, moreover, TA_UPDATECP is set in the DC
#if 0
		// if required, play the record
		SCPlayRecord();
#endif
		return SC_BRK_NOERROR;
	}

	UINT uiOptions = pRec->fOptions; // we might have to alter it
	// TODO: review this line
	BOOL bUnicode = (uiOptions == 0x100);

	// Build the string
	LPWSTR pwStr = NULL;
	if (bUnicode) 
	{
		// The string is in unicode -> we let it as is.
		// Offset is from the beginning of the struct and is in bytes.
		// 	
		// Get the unicode string
		pwStr = (LPWSTR)pTextRec->Text;

#if 0
		// TODO:
		// Translate glyph indices to code points when the rasterizer does not
		// support string measurement for glyph indices.
		if ((uiOptions & ETO_GLYPH_INDEX) && (!m_pRenderer->SCCanMeasureGlyphs())
		{
			int iRes = SCUnicodeCharsFromGlyphs(m_hPlayDC, pString, iNbChars);
			if (iRes)
				uiOptions &= ~ETO_GLYPH_INDEX;
		}
#endif
	} else
	{
		// The string is single-byte encoded.
		// Get adress of the string	then construct the string.			
		LPSTR pString = (LPSTR)pTextRec->Text;
		// TODO: review this line
		if (uiOptions==0x204)
			pString = (char *)pTextRec2->Text;

		pwStr = new wchar_t[iNbChars+1];
		if (pwStr)
		{// We will use UNICODE strings exclusively
			pwStr[iNbChars] = 0;
			int iNum = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pString, iNbChars, pwStr, iNbChars+1);
			#ifdef _DEBUG
			if (0==iNum)
			{
				DWORD dwError = GetLastError();
			}
			#endif
			ASSERT(iNum==iNbChars);
		}
	}
	ASSERT(pwStr);

	// Get intercharacter spacing array.
	// Be careful ! there are cases where OffDx==0.
	// TODO: review this line
	DWORD OffDx = 0;//pTextRec->offDx;
	INT *lpDx = (INT*)((BYTE*)pRec + OffDx);
	//if (0==OffDx)
	{
		// If the lpDx parameter is NULL, the ExtTextOut function uses the default
		// spacing between characters. So we do.
		//
		// Compute intercharacter spacing, based on each character width.
		lpDx = new INT[iNbChars];
		BOOL bOk = SCGetTextCharWidthsW(m_hPlayDC, pwStr, iNbChars, lpDx);
		ASSERT(bOk);
	} // else use distances between origins of adjacent character cells as passed to ExtTextOut.

	CRect rcText;
	if (uiOptions==0x204)
	{
		rcText.CopyRect((LPRECT)&pTextRec2->rclBoundsSC);
	} else
	{
		uiOptions &= ~ETO_CLIPPED; // we don't have the rectangle
		rcText.SetRectEmpty();
	}
	if (GM_COMPATIBLE==pTextRec->iGraphicsMode)
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPRECT)&rcText, pwStr, iNbChars, lpDx, pRec->exScale, pRec->eyScale);
	else
		m_pRenderer->SCDrawText(pTextRec->ptlReference.x, pTextRec->ptlReference.y, uiOptions,
		(LPRECT)&rcText, pwStr, iNbChars, lpDx, 1, 1);
	

	// Clean up
	if (!bUnicode)
		delete [] pwStr;
	if (0==OffDx)
		delete [] lpDx;

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfFORCEUFIMAPPING()
{
	// TRACE0("**EMR_FORCEUFIMAPPING\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// Danger if it writes to the DC
	// Worse if it attempts escape-like behavior
	//SCPlayRecord();

	// Anyway, now it's SCEMF_RESERVED_109
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfNAMEDESCAPE()
{
	// TRACE0("**EMR_NAMEDESCAPE\n");
	ASSERT(m_pRenderer);

	// Do not play escape
	// Anyway, now it's SCEMF_RESERVED_110
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCOLORCORRECTPALETTE()
{
	// TRACE0("**EMR_COLORCORRECTPALETTE\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMPROFILEA()
{
	// TRACE0("**EMR_SETICMPROFILEA\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETICMPROFILEW()
{
	// TRACE0("**EMR_SETICMPROFILEW\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfALPHABLEND()
{
	// TRACE0("**EMR_ALPHABLEND\n");
	ASSERT(m_pRenderer);

	EMRALPHABLEND *pRec = (EMRALPHABLEND*)m_pRecord;

	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);

	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// Destination
		CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);

		m_pRenderer->SCDrawImageAlpha(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc,
			pRec->dwRop, &pRec->xformSrc, pRec->crBkColorSrc);
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfALPHADIBBLEND()
{
	// TRACE0("**EMR_SETLAYOUT\n");
	ASSERT(m_pRenderer);

	// Now it's SCEMF_SETLAYOUT
	//DWORD SetLayout(
	//	HDC hdc,             // handle to DC
	//	DWORD dwLayout,      // layout options
	//	);
	// dwLayout==LAYOUT_BITMAPORIENTATIONPRESERVED Disables any reflection during BitBlt
	//											   and StretchBlt operations. 
	//           LAYOUT_RTL						   Sets the default horizontal layout
	//											   to be right to left. 
	// OSes: Windows 2000 and later, Windows 98 and later

	// TODO_EMF: horizontal reflection when the LAYOUT_RTL flag is selected
	// m_pRenderer->SCSetLayout(((EMRSETLAYOUT*)m_pRecord)->iMode);

	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfTRANSPARENTBLT()
{
	// TRACE0("**EMR_TRANSPARENTBLT\n");
	ASSERT(m_pRenderer);

	EMRTRANSPARENTBLT *pRec = (EMRTRANSPARENTBLT*)m_pRecord;
	ASSERT(pRec->offBmiSrc && pRec->offBitsSrc);

	// Check if there is a bitmap
	if (pRec->offBmiSrc && pRec->offBitsSrc)
	{
		// Source
		CRect RcSrc(pRec->xSrc, pRec->ySrc, pRec->xSrc + pRec->cxSrc, pRec->ySrc + pRec->cySrc);
		BITMAPINFO *pBmi    = (BITMAPINFO *) ((BYTE *)pRec + pRec->offBmiSrc); 
		BYTE *pBits     = (BYTE *) ((BYTE *) pRec + pRec->offBitsSrc);

		// Destination
		CRect RcDest(pRec->xDest, pRec->yDest, pRec->xDest + pRec->cxDest, pRec->yDest + pRec->cyDest);

		m_pRenderer->SCDrawImageTransparent(&RcDest, &RcSrc, pBits, pBmi, pRec->iUsageSrc,
			pRec->dwRop, &pRec->xformSrc, pRec->crBkColorSrc);
	}
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfTRANSPARENTDIB()
{
	// TRACE0("**EMR_TRANSPARENTDIB\n");
	ASSERT(m_pRenderer);

	// EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	// Anyway, now it's SCEMF_RESERVED_117
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfGRADIENTFILL()
{
	// TRACE0("**EMR_GRADIENTFILL\n");
	ASSERT(m_pRenderer);

	EMRGRADIENTFILL *pRec = (EMRGRADIENTFILL*)m_pRecord;

	LPBYTE pMesh = (LPBYTE)&pRec->Ver[pRec->nVer];
	m_pRenderer->SCGradientFill(pRec->Ver, pRec->nVer, pMesh, pRec->nTri, pRec->ulMode);
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETLINKEDUFIS()
{
	// TRACE0("**EMR_SETLINKEDUFIS\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	// Anyway, now it's SCEMF_RESERVED_119
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfSETTEXTJUSTIFICATION()
{
	// TRACE0("**EMR_SETTEXTJUSTIFICATION\n");
	ASSERT(m_pRenderer);

//		EMR *pRec = (EMR*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	// Anyway, now it's SCEMF_RESERVED_120
	return SC_BRK_NOERROR;
}

SC_BRKRESULT CSCEMFgdiParser::OnEmfCOLORMATCHTOTARGETW()
{
	// TRACE0("**EMR_CREATECOLORSPACE\n");
	ASSERT(m_pRenderer);

//		COLORMATCHTOTARGETW *pRec = (COLORMATCHTOTARGETW*)m_pRecord;

	// TODO_EMF: Place code here to handle EMF record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}


SC_BRKRESULT CSCEMFgdiParser::OnBrkUNKRecord(long lMsg)
{
	// TRACE0("**OnBrkUNKRecord\n");
	ASSERT(m_pRenderer);

	UNUSED(lMsg);   // unused in release builds

	// By default, play unkown record
	SCPlayRecord();
	return SC_BRK_NOERROR;
}

//////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////

// called before enumeration
SC_BRKRESULT CSCEMFgdiParser::SCBeginBreak(HDC hDC, LPVOID lpData, CONST RECT *lpRect)
{
	SC_BRKRESULT error(SC_BRK_NOERROR);

	ASSERT(m_pRenderer);
	if (!m_pRenderer)
		return SC_BRK_NORENDERER;

	GetWorldTransform(hDC, &m_xformOrig);

	// call inherited
	error = SCBrkEMF::SCBeginBreak(hDC, lpData, lpRect);

	// before enumeration
	// clone the DC so that this class does not write on the destination DC
	m_hDestDC = hDC;					 // for GDI+

#ifdef SC_USE_DCCLONER
	// TODO: fix the bitmap problem in the cloner
	m_pCloneDC = new SCDCCloner(hDC);
	ASSERT(m_pCloneDC);
	m_hPlayDC = m_pCloneDC->SCGetHDC();	 // for GDI
#else
	m_hPlayDC = hDC;					 // for GDI
#endif

	ASSERT(m_hPlayDC && m_hDestDC);
		
	return error;
}


//////////////////////////////////////////////////////////////////////
// Message map
//////////////////////////////////////////////////////////////////////
SC_BEGIN_MESSAGE_MAP(CSCEMFgdiParser, SCBrkEMF)
	ON_VECTRECORD(SCEMF_HEADER, OnEmfHEADER)
	ON_VECTRECORD(SCEMF_POLYBEZIER, OnEmfPOLYBEZIER)
	ON_VECTRECORD(SCEMF_POLYGON, OnEmfPOLYGON)
	ON_VECTRECORD(SCEMF_POLYLINE, OnEmfPOLYLINE)
	ON_VECTRECORD(SCEMF_POLYBEZIERTO, OnEmfPOLYBEZIERTO)
	ON_VECTRECORD(SCEMF_POLYLINETO, OnEmfPOLYLINETO)
	ON_VECTRECORD(SCEMF_POLYPOLYLINE, OnEmfPOLYPOLYLINE)
	ON_VECTRECORD(SCEMF_POLYPOLYGON, OnEmfPOLYPOLYGON)
	ON_VECTRECORD(SCEMF_SETWINDOWEXTEX, OnEmfSETWINDOWEXTEX)
	ON_VECTRECORD(SCEMF_SETWINDOWORGEX, OnEmfSETWINDOWORGEX)
	ON_VECTRECORD(SCEMF_SETVIEWPORTEXTEX, OnEmfSETVIEWPORTEXTEX)
	ON_VECTRECORD(SCEMF_SETVIEWPORTORGEX, OnEmfSETVIEWPORTORGEX)
	ON_VECTRECORD(SCEMF_SETBRUSHORGEX, OnEmfSETBRUSHORGEX)
	ON_VECTRECORD(SCEMF_EOF, OnEmfEOF)
	ON_VECTRECORD(SCEMF_SETPIXELV, OnEmfSETPIXELV)
	ON_VECTRECORD(SCEMF_SETMAPPERFLAGS, OnEmfSETMAPPERFLAGS)
	ON_VECTRECORD(SCEMF_SETMAPMODE, OnEmfSETMAPMODE)
	ON_VECTRECORD(SCEMF_SETBKMODE, OnEmfSETBKMODE)
	ON_VECTRECORD(SCEMF_SETPOLYFILLMODE, OnEmfSETPOLYFILLMODE)
	ON_VECTRECORD(SCEMF_SETROP2, OnEmfSETROP2)
	ON_VECTRECORD(SCEMF_SETSTRETCHBLTMODE, OnEmfSETSTRETCHBLTMODE)
	ON_VECTRECORD(SCEMF_SETTEXTALIGN, OnEmfSETTEXTALIGN)
	ON_VECTRECORD(SCEMF_SETCOLORADJUSTMENT, OnEmfSETCOLORADJUSTMENT)
	ON_VECTRECORD(SCEMF_SETTEXTCOLOR, OnEmfSETTEXTCOLOR)
	ON_VECTRECORD(SCEMF_SETBKCOLOR, OnEmfSETBKCOLOR)
	ON_VECTRECORD(SCEMF_OFFSETCLIPRGN, OnEmfOFFSETCLIPRGN)
	ON_VECTRECORD(SCEMF_MOVETOEX, OnEmfMOVETOEX)
	ON_VECTRECORD(SCEMF_SETMETARGN, OnEmfSETMETARGN)
	ON_VECTRECORD(SCEMF_EXCLUDECLIPRECT, OnEmfEXCLUDECLIPRECT)
	ON_VECTRECORD(SCEMF_INTERSECTCLIPRECT, OnEmfINTERSECTCLIPRECT)
	ON_VECTRECORD(SCEMF_SCALEVIEWPORTEXTEX, OnEmfSCALEVIEWPORTEXTEX)
	ON_VECTRECORD(SCEMF_SCALEWINDOWEXTEX, OnEmfSCALEWINDOWEXTEX)
	ON_VECTRECORD(SCEMF_SAVEDC, OnEmfSAVEDC)
	ON_VECTRECORD(SCEMF_RESTOREDC, OnEmfRESTOREDC)
	ON_VECTRECORD(SCEMF_SETWORLDTRANSFORM, OnEmfSETWORLDTRANSFORM)
	ON_VECTRECORD(SCEMF_MODIFYWORLDTRANSFORM, OnEmfMODIFYWORLDTRANSFORM)
	ON_VECTRECORD(SCEMF_SELECTOBJECT, OnEmfSELECTOBJECT)
	ON_VECTRECORD(SCEMF_CREATEPEN, OnEmfCREATEPEN)
	ON_VECTRECORD(SCEMF_CREATEBRUSHINDIRECT, OnEmfCREATEBRUSHINDIRECT)
	ON_VECTRECORD(SCEMF_DELETEOBJECT, OnEmfDELETEOBJECT)
	ON_VECTRECORD(SCEMF_ANGLEARC, OnEmfANGLEARC)
	ON_VECTRECORD(SCEMF_ELLIPSE, OnEmfELLIPSE)
	ON_VECTRECORD(SCEMF_RECTANGLE, OnEmfRECTANGLE)
	ON_VECTRECORD(SCEMF_ROUNDRECT, OnEmfROUNDRECT)
	ON_VECTRECORD(SCEMF_ARC, OnEmfARC)
	ON_VECTRECORD(SCEMF_CHORD, OnEmfCHORD)
	ON_VECTRECORD(SCEMF_PIE, OnEmfPIE)
	ON_VECTRECORD(SCEMF_SELECTPALETTE, OnEmfSELECTPALETTE)
	ON_VECTRECORD(SCEMF_CREATEPALETTE, OnEmfCREATEPALETTE)
	ON_VECTRECORD(SCEMF_SETPALETTEENTRIES, OnEmfSETPALETTEENTRIES)
	ON_VECTRECORD(SCEMF_RESIZEPALETTE, OnEmfRESIZEPALETTE)
	ON_VECTRECORD(SCEMF_REALIZEPALETTE, OnEmfREALIZEPALETTE)
	ON_VECTRECORD(SCEMF_EXTFLOODFILL, OnEmfEXTFLOODFILL)
	ON_VECTRECORD(SCEMF_LINETO, OnEmfLINETO)
	ON_VECTRECORD(SCEMF_ARCTO, OnEmfARCTO)
	ON_VECTRECORD(SCEMF_POLYDRAW, OnEmfPOLYDRAW)
	ON_VECTRECORD(SCEMF_SETARCDIRECTION, OnEmfSETARCDIRECTION)
	ON_VECTRECORD(SCEMF_SETMITERLIMIT, OnEmfSETMITERLIMIT)
	ON_VECTRECORD(SCEMF_BEGINPATH, OnEmfBEGINPATH)
	ON_VECTRECORD(SCEMF_ENDPATH, OnEmfENDPATH)
	ON_VECTRECORD(SCEMF_CLOSEFIGURE, OnEmfCLOSEFIGURE)
	ON_VECTRECORD(SCEMF_FILLPATH, OnEmfFILLPATH)
	ON_VECTRECORD(SCEMF_STROKEANDFILLPATH, OnEmfSTROKEANDFILLPATH)
	ON_VECTRECORD(SCEMF_STROKEPATH, OnEmfSTROKEPATH)
	ON_VECTRECORD(SCEMF_FLATTENPATH, OnEmfFLATTENPATH)
	ON_VECTRECORD(SCEMF_WIDENPATH, OnEmfWIDENPATH)
	ON_VECTRECORD(SCEMF_SELECTCLIPPATH, OnEmfSELECTCLIPPATH)
	ON_VECTRECORD(SCEMF_ABORTPATH, OnEmfABORTPATH)
	ON_VECTRECORD(SCEMF_GDICOMMENT, OnEmfGDICOMMENT)
	ON_VECTRECORD(SCEMF_FILLRGN, OnEmfFILLRGN)
	ON_VECTRECORD(SCEMF_FRAMERGN, OnEmfFRAMERGN)
	ON_VECTRECORD(SCEMF_INVERTRGN, OnEmfINVERTRGN)
	ON_VECTRECORD(SCEMF_PAINTRGN, OnEmfPAINTRGN)
	ON_VECTRECORD(SCEMF_EXTSELECTCLIPRGN, OnEmfEXTSELECTCLIPRGN)
	ON_VECTRECORD(SCEMF_BITBLT, OnEmfBITBLT)
	ON_VECTRECORD(SCEMF_STRETCHBLT, OnEmfSTRETCHBLT)
	ON_VECTRECORD(SCEMF_MASKBLT, OnEmfMASKBLT)
	ON_VECTRECORD(SCEMF_PLGBLT, OnEmfPLGBLT)
	ON_VECTRECORD(SCEMF_SETDIBITSTODEVICE, OnEmfSETDIBITSTODEVICE)
	ON_VECTRECORD(SCEMF_STRETCHDIBITS, OnEmfSTRETCHDIBITS)
	ON_VECTRECORD(SCEMF_EXTCREATEFONTINDIRECTW, OnEmfEXTCREATEFONTINDIRECTW)
	ON_VECTRECORD(SCEMF_EXTTEXTOUTA, OnEmfEXTTEXTOUTA)
	ON_VECTRECORD(SCEMF_EXTTEXTOUTW, OnEmfEXTTEXTOUTW)
	ON_VECTRECORD(SCEMF_POLYBEZIER16, OnEmfPOLYBEZIER16)
	ON_VECTRECORD(SCEMF_POLYGON16, OnEmfPOLYGON16)
	ON_VECTRECORD(SCEMF_POLYLINE16, OnEmfPOLYLINE16)
	ON_VECTRECORD(SCEMF_POLYBEZIERTO16, OnEmfPOLYBEZIERTO16)
	ON_VECTRECORD(SCEMF_POLYLINETO16, OnEmfPOLYLINETO16)
	ON_VECTRECORD(SCEMF_POLYPOLYLINE16, OnEmfPOLYPOLYLINE16)
	ON_VECTRECORD(SCEMF_POLYPOLYGON16, OnEmfPOLYPOLYGON16)
	ON_VECTRECORD(SCEMF_POLYDRAW16, OnEmfPOLYDRAW16)
	ON_VECTRECORD(SCEMF_CREATEMONOBRUSH, OnEmfCREATEMONOBRUSH)
	ON_VECTRECORD(SCEMF_CREATEDIBPATTERNBRUSHPT, OnEmfCREATEDIBPATTERNBRUSHPT)
	ON_VECTRECORD(SCEMF_EXTCREATEPEN, OnEmfEXTCREATEPEN)
	ON_VECTRECORD(SCEMF_POLYTEXTOUTA, OnEmfPOLYTEXTOUTA)
	ON_VECTRECORD(SCEMF_POLYTEXTOUTW, OnEmfPOLYTEXTOUTW)
	ON_VECTRECORD(SCEMF_SETICMMODE, OnEmfSETICMMODE)
	ON_VECTRECORD(SCEMF_CREATECOLORSPACE, OnEmfCREATECOLORSPACE)
	ON_VECTRECORD(SCEMF_SETCOLORSPACE, OnEmfSETCOLORSPACE)
	ON_VECTRECORD(SCEMF_DELETECOLORSPACE, OnEmfDELETECOLORSPACE)
	ON_VECTRECORD(SCEMF_GLSRECORD, OnEmfGLSRECORD)
	ON_VECTRECORD(SCEMF_GLSBOUNDEDRECORD, OnEmfGLSBOUNDEDRECORD)
	ON_VECTRECORD(SCEMF_PIXELFORMAT, OnEmfPIXELFORMAT)
	ON_VECTRECORD(SCEMF_DRAWESCAPE, OnEmfDRAWESCAPE)
	ON_VECTRECORD(SCEMF_EXTESCAPE, OnEmfEXTESCAPE)
	ON_VECTRECORD(SCEMF_STARTDOC, OnEmfSTARTDOC)
	ON_VECTRECORD(SCEMF_SMALLTEXTOUT, OnEmfSMALLTEXTOUT)
	ON_VECTRECORD(SCEMF_FORCEUFIMAPPING, OnEmfFORCEUFIMAPPING)
	ON_VECTRECORD(SCEMF_NAMEDESCAPE, OnEmfNAMEDESCAPE)
	ON_VECTRECORD(SCEMF_COLORCORRECTPALETTE, OnEmfCOLORCORRECTPALETTE)
	ON_VECTRECORD(SCEMF_SETICMPROFILEA, OnEmfSETICMPROFILEA)
	ON_VECTRECORD(SCEMF_SETICMPROFILEW, OnEmfSETICMPROFILEW)
	ON_VECTRECORD(SCEMF_ALPHABLEND, OnEmfALPHABLEND)
	ON_VECTRECORD(SCEMF_ALPHADIBBLEND, OnEmfALPHADIBBLEND)
	ON_VECTRECORD(SCEMF_TRANSPARENTBLT, OnEmfTRANSPARENTBLT)
	ON_VECTRECORD(SCEMF_TRANSPARENTDIB, OnEmfTRANSPARENTDIB)
	ON_VECTRECORD(SCEMF_GRADIENTFILL, OnEmfGRADIENTFILL)
	ON_VECTRECORD(SCEMF_SETLINKEDUFIS, OnEmfSETLINKEDUFIS)
	ON_VECTRECORD(SCEMF_SETTEXTJUSTIFICATION, OnEmfSETTEXTJUSTIFICATION)
	ON_VECTRECORD(SCEMF_COLORMATCHTOTARGETW, OnEmfCOLORMATCHTOTARGETW)
	ON_VECTRECORD(SCEMF_CREATECOLORSPACEW, OnEmfCREATECOLORSPACEW)
SC_END_MESSAGE_MAP()